#include "stdafx.h"
#include "stdio.h"
#include "gcryptHelper.h"
#include "gnutlsHelper.h"
// libgnutlsHelper.cpp : Defines the exported functions for the DLL application.
//
extern "C"
{

	// self defined
	struct gnutls_session_int;
	typedef struct
	{
		unsigned char *data;
		unsigned int size;
	} gnutls_datum_t;

	/*** Type define from gnutls****/

	typedef void *(*gnutls_alloc_function) (size_t);
	typedef void *(*gnutls_calloc_function) (size_t, size_t);
	typedef int (*gnutls_is_secure_function) (const void *);
	typedef void (*gnutls_free_function) (void *);
	typedef void *(*gnutls_realloc_function) (void *, size_t);

	typedef int (*gnutls_db_store_func) (void *, gnutls_datum_t key, gnutls_datum_t data);
	typedef int (*gnutls_db_remove_func) (void *, gnutls_datum_t key);
	typedef gnutls_datum_t (*gnutls_db_retr_func) (void *, gnutls_datum_t key);
	void gnutls_db_set_cache_expiration (gnutls_session_t session, int seconds);

	void gnutls_db_remove_session (gnutls_session_t session);
	void gnutls_db_set_retrieve_function (gnutls_session_t session,	gnutls_db_retr_func retr_func);
	void gnutls_db_set_remove_function (gnutls_session_t session,	gnutls_db_remove_func rem_func);
	void gnutls_db_set_store_function (gnutls_session_t session,	gnutls_db_store_func store_func);
	void gnutls_db_set_ptr (gnutls_session_t session, void *ptr);

	gnutls_alloc_function gnutls_malloc;

	/*** End of Type define from gnutls***/


	/*** Session Resume Support *****/


#define SESSION_ID_SIZE 32
#define SESSION_DATA_SIZE 1024
	static unsigned int ssl_session_cache = 128;
	static void wrap_db_init (unsigned int size);
	static void wrap_db_deinit (void);
	static int wrap_db_store (void *dbf, gnutls_datum_t key, gnutls_datum_t data);
	static gnutls_datum_t wrap_db_fetch (void *dbf, gnutls_datum_t key);
	static int wrap_db_delete (void *dbf, gnutls_datum_t key);

	typedef struct
	{
		char session_id[SESSION_ID_SIZE];
		unsigned int session_id_size;

		char session_data[SESSION_DATA_SIZE];
		unsigned int session_data_size;
	} CACHE;

	static CACHE *cache_db;
	static int cache_db_ptr = 0;

	static void wrap_db_init (unsigned int size)
	{
		ssl_session_cache = size;
		/* allocate cache_db */
		cache_db = (CACHE*)malloc( ssl_session_cache * sizeof (CACHE));
	}

	static void wrap_db_deinit (void)
	{
	}

	static int wrap_db_store (void *dbf, gnutls_datum_t key, gnutls_datum_t data)
	{

		if (cache_db == NULL)
			return -1;

		if (key.size > SESSION_ID_SIZE)
			return -1;
		if (data.size > SESSION_DATA_SIZE)
			return -1;

		memcpy (cache_db[cache_db_ptr].session_id, key.data, key.size);
		cache_db[cache_db_ptr].session_id_size = key.size;

		memcpy (cache_db[cache_db_ptr].session_data, data.data, data.size);
		cache_db[cache_db_ptr].session_data_size = data.size;

		cache_db_ptr++;
		cache_db_ptr %= ssl_session_cache;

		return 0;
	}

	static gnutls_datum_t wrap_db_fetch (void *dbf, gnutls_datum_t key)
	{
		gnutls_datum_t res = { NULL, 0 };
		int i;

		if (cache_db == NULL)
			return res;

		for (i = 0; i < ssl_session_cache; i++)
		{
			if (key.size == cache_db[i].session_id_size &&
				memcmp (key.data, cache_db[i].session_id, key.size) == 0)
			{
				res.size = cache_db[i].session_data_size;

				res.data = (unsigned char*)malloc(res.size);
				if (res.data == NULL)
					return res;

				memcpy (res.data, cache_db[i].session_data, res.size);

				return res;
			}
		}
		return res;
	}

	static int		wrap_db_delete (void *dbf, gnutls_datum_t key)
	{
		int i;

		if (cache_db == NULL)
			return -1;

		for (i = 0; i < ssl_session_cache; i++)
		{
			if (key.size == (unsigned int) cache_db[i].session_id_size &&
				memcmp (key.data, cache_db[i].session_id, key.size) == 0)
			{

				cache_db[i].session_id_size = 0;
				cache_db[i].session_data_size = 0;

				return 0;
			}
		}

		return -1;
	}
	__declspec( dllexport ) void init_session_resume(unsigned int size)
	{
		wrap_db_init(size);
	}
	/** End of session resume support ***/




	__declspec( dllexport ) void setup_libgcrypt_mutext_callback()
	{
		gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
		init_session_resume();
	}


	__declspec( dllexport ) void set_session_db(gnutls_session_t session)
	{
		gnutls_db_set_retrieve_function (session, wrap_db_fetch);
		gnutls_db_set_remove_function (session, wrap_db_delete);
		gnutls_db_set_store_function (session, wrap_db_store);
		gnutls_db_set_ptr (session, NULL);
	}
	void gnutls_global_set_mem_functions(gnutls_alloc_function alloc_func, gnutls_alloc_function secure_alloc_func, gnutls_is_secure_function is_secure_func, gnutls_realloc_function realloc_func, gnutls_free_function free_func);

	__declspec( dllexport ) void set_default_mem_ptr()
	{
		gnutls_global_set_mem_functions(malloc,malloc,NULL,realloc,free);
	}
}
